#include "Utils.h"

#include <algorithm>
#include <cmath>

#include "Vector3.h"
#include <random>

bool areEquals(const float num1, 
               const float num2, 
               const float epsilon /*= 1.0E-5*/) 
{
    const float s = fabs(num1 - num2);

    return s < epsilon;
}

void unitVectorFromOrientation(const float orientation, Vector3& v) {
    v.x = sin(orientation);
    v.y = 0.0f;
    v.z = cos(orientation);
}

float orientationFromVector(const Vector3& v) {
    const float len = v.length();
    if (areEquals(len, 0.0f)) {
        return 0.0f;
    }

    // Calculate orientation using arc tangent
    // of vec components
    return static_cast<float> (atan2(v.x, v.z));
}

float rotationAngle(const Vector3& src, const Vector3& dest) {
    Vector3 unitSrc;
    normalize(src, unitSrc);

    Vector3 unitDest;
    normalize(dest, unitDest);

    Vector3 crossVec;
    crossProduct(unitSrc, unitDest, crossVec);         
    const bool isZeroVec =crossVec. isZero();
    const float dot = dotProduct(unitSrc, unitDest);

    // src and dest are aligned
    if (isZeroVec && areEquals(dot, 0.0f)) {
        return 0.0f;
    }

    // If crossVec is pointing out of the screen,
    // then we should rotate counterclockwise
    // from dest to src. Otherwise we
    // should rotate clockwise
    const float angle = acos(dot);
    return (crossVec.y > 0.0f) ? angle : -angle;        
}

void rotateAroundYAxis(Vector3& v, const float angle) {
    const float cosAngle = cos(angle);
    const float sinAngle = sin(angle);
    const Vector3 yAxis(0.0f, 1.0f, 0.0f);
    const float dotProd = dotProduct(yAxis, v);

    // vec * cos      
    Vector3 v1(v.x * cosAngle, v.y * cosAngle, v.z * cosAngle);

    // (yAxis x vec) * sin
    Vector3 v2;
    crossProduct(yAxis, v, v2);
    v2 *= sinAngle;

    Vector3 v3(yAxis);
    v3 = yAxis * (dotProd * (1.0f - cosAngle));

    v = v1;
    v += v2;
    v += v3;
}

float randomFloat(const float max /*= 1.0f*/) {
    const float randMax = static_cast<float> (RAND_MAX);
    const float randNumber = static_cast<float> (rand());
    return max * randNumber / randMax;
}

float randomBinomial(const float max /*= 1.0f*/) {
    return randomFloat(max) - randomFloat(max);
}

int randomInt(int min, int max) {
	std::random_device rd;
    std::default_random_engine e1(rd());
    std::uniform_int_distribution<int> uniform_dist(min, max);
    return uniform_dist(e1);
}